package priv.chenkai.spring_cli.common.log;

import com.alibaba.fastjson.JSON;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Aspect
@Component
public class SysLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(SysLogAspect.class);

    /**
     * 定义一个方法，用于声明切入点表达式，一般的，该方法中不再不要填土其他的代码。
     * 使用@Pointcut来声明切入点表达式。
     * 同一个类中其他通知直接使用方法名来引用当前的切入点表达式，如：@Before("method()")
     * 同一个报下其他类中的通知需要在方法名前加类名，如：@Before("class.method()")
     * 其他包下面类中的通知需要在方法名前加类的全额限定名,如：@AfterReturning(value="package.class.method()",returning="result")
     * <p>
     * 第一个星号代表匹配任意修饰符及任意返回值, 第二个星号表示任意方法名称，参数列表中的两个点号表示任意数量和类型的参数
     */
    @Pointcut("@annotation(priv.chenkai.spring_cli.common.log.OpLog)")
    public void controllerAspect() { }

    /**
     * 所有insert方法
     */
    @Pointcut("execution(* *.insert(..))")
    public void serviceAspect(){ }

    //    @Before("controllerAspect()")
    public void doBefore(JoinPoint joinPoint) {
        System.out.println("=====SysLogAspect前置通知开始=====");
        Object[] arguments = joinPoint.getArgs();
        logger.info("arguments:{}", JSON.toJSONString(arguments));
        handleLog(joinPoint, null);
    }

    /**
     * 配置controller的环绕通知
     * 环绕通知类似于动态代理的全过程：ProceedingJoinPoint类型的参数可以决定是否执行目标方法
     * 且环绕通知必须有返回值，返回值即为目标方法的返回值
     *
     * @param joinPoint
     */
    @Around("controllerAspect()")
    public Object around(JoinPoint joinPoint) {
        System.out.println("==========开始执行controller环绕通知===============");
        Object result = null;
        long start = System.currentTimeMillis();
        try {
            //before
            logger.info("请求的参数为：{}", JSON.toJSON(joinPoint.getArgs()));
            result = ((ProceedingJoinPoint) joinPoint).proceed();
            //after
            long end = System.currentTimeMillis();
            if (logger.isInfoEnabled()) {
                logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms!");
            }
            System.out.println("==========结束执行controller环绕通知===============");
        } catch (Throwable e) {
            e.printStackTrace();
            long end = System.currentTimeMillis();
            if (logger.isInfoEnabled()) {
                logger.info("around " + joinPoint + "\tUse time : " + (end - start) + " ms with exception : " + e.getMessage());
            }
        }
        return result;
    }

    /**
     * 后置通知 用于拦截Controller层记录用户的操作
     */
//    @After("controllerAspect()")
    public void after(JoinPoint joinPoint) {
        logger.info("后置通知...");
//        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//        HttpSession session = request.getSession();
        //TODO 根据session获取用户id

    }

    @AfterReturning(pointcut = "controllerAspect()")
    public void doAfterReturn(JoinPoint joinPoint) {
        System.out.println("=====SysLogAspect后置通知开始=====");
        handleLog(joinPoint, null);
    }

    @AfterThrowing(value = "controllerAspect()", throwing = "e")
    public void doAfter(JoinPoint joinPoint, Exception e) {
        System.out.println("=====SysLogAspect异常通知开始=====");
        handleLog(joinPoint, e);
    }

    @Before(value = "serviceAspect()")
    public void aroundService(JoinPoint joinPoint){
        handleLog(joinPoint,null);
        logger.info("service before aspect");
    }

    private void handleLog(JoinPoint joinPoint, Exception e) {
        try {
            //获得注解
            OpLog opLog = giveController(joinPoint);
            if (logger == null) {
                return;
            }

            String signature = joinPoint.getSignature().toString(); // 获取目标方法签名
            String methodName = signature.substring(signature.lastIndexOf(".") + 1,
                    signature.indexOf("("));

            String longTemp = joinPoint.getStaticPart().toLongString();
            String classType = joinPoint.getTarget().getClass().getName();

            Class<?> clazz = Class.forName(classType);

            Method[] methods = clazz.getDeclaredMethods();
            System.out.println("methodName: " + methodName);

            for (Method method : methods) {
                if (method.isAnnotationPresent(OpLog.class)
                        && method.getName().equals(methodName)) {
                    String annId = opLog.id();
                    String clazzName = clazz.getName();
                    System.out.println("clazzName: " + clazzName + ", methodName: "
                            + methodName + ", annId: " + annId + ", type: " + opLog.type().toString());
                }
            }

        } catch (Exception exp) {
            logger.error("异常信息:{}", exp.getMessage());
            exp.printStackTrace();
        }
    }

    /**
     * 获取注解内容
     *
     * @param joinPoint
     * @return
     * @throws Exception
     */
    private static OpLog giveController(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(OpLog.class);
        }
        return null;
    }


    public void insertLogSuccess(JoinPoint jp, OpLog logger) {
    }

    public void writeLogInfo(JoinPoint joinPoint, OpLog opLogger)
            throws Exception, IllegalAccessException {
    }
}
